﻿using Conqueror.API;
using Conqueror.WP.Utility;
using Conqueror.WP.ViewModel.Matchmaking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Conqueror.WP.Matchmaking
{
    public static class Matchmaker
    {
        private const int maximumPlayersToTryNumber = 30;
        private const int maximumSelectNumber = 4;

        public static async Task<IEnumerable<MatchmakingResultViewModel>> FindMatches(MatchmakingParameters p)
        {
            return await Task.Run<IEnumerable<MatchmakingResultViewModel>>(async () => {
                List<MatchmakingResultViewModel> results = new List<MatchmakingResultViewModel>();

                string players = "";
                for(int i = p.MinPlayers; i <= p.MaxPlayers; ++i)
                {
                    players += i + ",";
                }
                players.TrimEnd(',');
                List<Game> games = await Game.GetGames(null, GameState.Waiting, players, p.GameType, p.Map, p.InitialTroops, p.PlayOrder, p.Spoils, p.Fortifications, p.FogOfWar, p.TrenchWarfare);
                Dictionary<string, Player> chosenPlayers = new Dictionary<string, Player>();
                List<Game> selectedGames = new List<Game>();
                List<Game> potentialGames = new List<Game>();

                foreach(Game g in games)
                {
                    if (maximumPlayersToTryNumber - (chosenPlayers.Count + g.Players.Players.Count) < p.MinPlayers) break; //Dont keep searching if no other game can possible fill the role.
                    if (chosenPlayers.Count + g.Players.Players.Count > maximumPlayersToTryNumber) continue; //If game has too many players
                    foreach(GamePlayer player in g.Players.Players)
                    {
                        if (string.IsNullOrEmpty(player.Name))
                            continue;
                        if (!chosenPlayers.ContainsKey(player.Name.ToLower()))
                        {
                            chosenPlayers.Add(player.Name.ToLower(), null); //Fill with null currently, the next step finds all players
                        }
                    }
                    potentialGames.Add(g);
                }
                chosenPlayers.Add(User.Username.ToLower(), null);

                //Get player details.
                //First build player name list
                IEnumerable<Player> playerResults = await Player.GetPlayersAsync(chosenPlayers.Keys);
                foreach(Player player in playerResults)
                {
                    chosenPlayers.Remove(player.Username.ToLower());
                    chosenPlayers.Add(player.Username.ToLower(), player);
                }

                Player currentPlayer = chosenPlayers[User.Username.ToLower()];
                Dictionary<string, int> gameDifficulty = new Dictionary<string,int>();
                foreach (Game g in potentialGames)
                {
                    List<Player> currentPlayers = new List<Player>();
                    foreach(GamePlayer player in g.Players.Players)
                    {
                        if (string.IsNullOrEmpty(player.Name))
                            continue;
                        currentPlayers.Add(chosenPlayers[player.Name.ToLower()]);
                        player.Meta = chosenPlayers[player.Name.ToLower()].Rank;
                    }

                    int difficultyAverage = 0;
                    foreach(Player player in currentPlayers)
                    {
                        difficultyAverage += player.Score;
                    }
                    difficultyAverage /= currentPlayers.Count; //we now have the average player score in this game.
                    gameDifficulty.Add(g.GameNumber, difficultyAverage);
                }

                var orderedGames = gameDifficulty.OrderBy((kv) => Math.Abs(currentPlayer.Score - kv.Value));
                foreach(var game in orderedGames)
                {
                    if (selectedGames.Count >= maximumSelectNumber)
                        break;
                    selectedGames.Add(potentialGames.Where((g) => g.GameNumber == game.Key).First());
                }

                foreach(Game g in selectedGames)
                {
                    results.Add(new MatchmakingResultViewModel(g));
                }

                return results.AsEnumerable();
            });
        }
    }
}
